package com.onpositive.mediawiki.propertyparser;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map.Entry;

import com.onpositive.data.units.Unit;
import com.onpositive.data.units.Units;
import com.onpositive.semantic.model.api.changes.IValueListener;
import com.onpositive.semantic.model.api.labels.ITextLabelProvider;
import com.onpositive.semantic.model.api.meta.IHasMeta;
import com.onpositive.semantic.model.api.realm.AbstractFilter;
import com.onpositive.semantic.model.api.realm.IFilter;
import com.onpositive.semantic.model.tree.IClusterizationPoint;
import com.onpositive.semantic.model.tree.ITreeNode;
import com.onpositive.units.ParsedDimension;
import com.onpositive.units.ParsedFileList;
import com.onpositive.units.ParsedLinkArray;
import com.onpositive.units.ParsedProperty;
import com.onpositive.units.ParsedValue;

public class PropertyCharacteristic {
	
	private static HashSet<String> multivalueProperties ;
	static
	{
		multivalueProperties = new HashSet<String>() ;
		multivalueProperties.add( ParsedLinkArray.ID ) ;
		multivalueProperties.add( ParsedFileList.ID ) ;		
	}

	
	public static final String[] CHARACTERISTICS_LABELS_ARRAY = { 
		"property-name",
		"parser-id",
		"samples-count",
		"rate",
		"depricated",
		"unit", 
		"group",
		"accepts-disambiguation",
		"unique-values-count",
		"primary-unit",
		"is-multivalue",
		"unit-kind",
		"is-service-property",
		"is-ident"
	};

	public static final String CHAR_PROPERTY_NAME_LABEL		= CHARACTERISTICS_LABELS_ARRAY[ 0] ;
	public static final String CHAR_PARSER_ID_LABEL     	= CHARACTERISTICS_LABELS_ARRAY[ 1] ;
	public static final String CHAR_SAMPLES_COUNT_LABEL 	= CHARACTERISTICS_LABELS_ARRAY[ 2] ;
	public static final String CHAR_RATE_LABEL          	= CHARACTERISTICS_LABELS_ARRAY[ 3] ;
	public static final String CHAR_DEPRICATED_LABEL    	= CHARACTERISTICS_LABELS_ARRAY[ 4] ;
	public static final String CHAR_UNIT_LABEL				= CHARACTERISTICS_LABELS_ARRAY[ 5] ;
	public static final String CHAR_GROUP_LABEL    	    	= CHARACTERISTICS_LABELS_ARRAY[ 6] ;
	public static final String CHAR_ACCEPTS_DISAMBIGUATION 	= CHARACTERISTICS_LABELS_ARRAY[ 7] ;
	public static final String CHAR_UNIQUE_VALUE_COUNT	 	= CHARACTERISTICS_LABELS_ARRAY[ 8] ;
	public static final String CHAR_PRIMARY_UNIT_LABEL	 	= CHARACTERISTICS_LABELS_ARRAY[ 9] ;
	public static final String CHAR_IS_MULTIVALUE_LABEL	 	= CHARACTERISTICS_LABELS_ARRAY[10] ;
	public static final String CHAR_UNIT_KIND_LABEL	 		= CHARACTERISTICS_LABELS_ARRAY[11] ;
	public static final String CHAR_IS_SERVICE_PROPERTY		= CHARACTERISTICS_LABELS_ARRAY[12] ;
	public static final String CHAR_IS_IDENTIFICATOR		= CHARACTERISTICS_LABELS_ARRAY[13] ;
	
	private static final String GROUP_SEPARATOR = ";" ;
	
	private ParsedCategory category ;
	private HashMap<String, String> storage = new HashMap<String, String>() ;
	
	private HashSet<PropertyGroup> groups = null ;
	private HashSet<String> groupNames = new HashSet<String>() ;
	

	private boolean depricated = false ;
	private boolean isIdent = false ;
	

	private boolean acceptsDisambiguation = false ;
	private boolean isServiceProperty = false ;

	private int uniqueValuesCount ;
	
	public HashSet<PropertyGroup> getGroups()
	{
		if( groups == null || groups.size() == 0 )
		{
			if( category == null )
				return null ;
			
			updateGroups() ;
		}
		
		return groups;
	}

	private void updateGroups()
	{
		if( category == null )
			return ;
		
		if( groups == null )
			groups = new HashSet<PropertyGroup>() ;
		
		groups.clear() ;
		for( String n : groupNames )
			groups.add( category.getPropertyGroup(n)) ;		
	}
	
	private void updateGroupNames(){
		
		if( groups == null )
			return ;
		
		groupNames.clear() ;
		for( PropertyGroup gr : groups )
			groupNames.add( gr.getName().trim() ) ;		
	}


	
	public String getCharacteristic( String id )
	{
		if( id.equals( CHAR_DEPRICATED_LABEL ) )
			return ((Boolean)depricated ).toString() ;
		
		if( id.equals( CHAR_ACCEPTS_DISAMBIGUATION ) )
			return ((Boolean)acceptsDisambiguation ).toString() ;
		
		if( id.equals( CHAR_GROUP_LABEL ) )
			return getGroupString() ;
		
		if( id.equals( CHAR_UNIQUE_VALUE_COUNT ) )
			return NumberFormat.getInstance( Locale.US ).format( this.uniqueValuesCount ) ;
		
		if( id.equals( CHAR_PRIMARY_UNIT_LABEL ) )
			return getPrimaryUnitShortName();
		
		if( id.equals( CHAR_IS_MULTIVALUE_LABEL ) )
			return multivalueProperties.contains( getCharacteristic(CHAR_PARSER_ID_LABEL)) ? "true" : "false" ;
		
		if( id.equals( CHAR_IS_SERVICE_PROPERTY ) )
			return ((Boolean) isServiceProperty).toString() ;
		
		if( id.equals( CHAR_IS_IDENTIFICATOR ) )
			return ((Boolean) isIdent).toString() ;
		
		return storage.get(id) ;
	}

	
	public String getName(){ return storage.get( CHAR_PROPERTY_NAME_LABEL ) ; }
	
	
	public int getUniqueValuesCount() {
		return uniqueValuesCount;
	}

	public void setUniqueValuesCount(int uniqueValuesCount) {
		this.uniqueValuesCount = uniqueValuesCount;
	}

	public void setCharacteristic( String id, String value )
	{
		if( id.equals( CHAR_DEPRICATED_LABEL ) ){
			depricated = Boolean.parseBoolean( value ) ;
			return ;
		}
		if( id.equals( CHAR_ACCEPTS_DISAMBIGUATION ) ){
			acceptsDisambiguation = Boolean.parseBoolean( value ) ;
			return ;
		}
		if( id.equals( CHAR_UNIQUE_VALUE_COUNT ) ){
			try {
				uniqueValuesCount = NumberFormat.getInstance( Locale.US ).parse( value ).intValue() ;
			} catch (ParseException e) {
				e.printStackTrace();
			}
			return ;
		}
		if( id.equals( CHAR_GROUP_LABEL ) ){
			setGroupString( value ) ;
			return ;
		}
		if( id.equals( CHAR_IS_SERVICE_PROPERTY ) ){
			isServiceProperty = Boolean.parseBoolean( value ) ;
			return ;
		}
		if( id.equals( CHAR_IS_IDENTIFICATOR ) ){
			isIdent = Boolean.parseBoolean( value ) ;
			return ;
		}
		
		storage.put( id, value) ;
	}
	
	public String getGroupString(){
		
		if ( groupNames.size() == 0 )
			return "" ;
		
		String result = "" ;
		
		for( String gr : groupNames )
			result += gr + " " + GROUP_SEPARATOR ;
		
		return result.substring( 0, result.length() - GROUP_SEPARATOR.length() ).trim() ;
	}
	
	public void setGroupString( String _s )
	{
		String s = _s.trim() ;
		if( s == null || s.length() == 0 )
			return ;
		
		String[] grArr = s.split( GROUP_SEPARATOR ) ;
		groupNames.clear() ;
		for( String gr : grArr )
			if( gr.length() > 0 )
				groupNames.add( gr.trim() ) ;
		
		updateGroups() ;
	}
	
	public void setGroups( Collection<PropertyGroup> groups) {
		
		if( this.groups == null )
			this.groups = new HashSet<PropertyGroup>() ;
		else
			this.groups.clear() ;
		
		for( PropertyGroup pg : groups )
			this.groups.add( pg ) ;

		updateGroupNames() ;
	}
	
	public HashSet<String> getGroupNames()
	{
		return this.groupNames ;
	}
	
	public boolean getDepricated(){
		return depricated ;
	}
	
	public boolean isAcceptsDisambiguation() {
		return acceptsDisambiguation;
	}
	
	public void setDepricated( boolean depricated ){
		this.depricated = depricated ;
	}
	
	public String getDepr(){
		return ((Boolean)depricated).toString() ;
	}
	
	public void setDepr( String depr ){
		this.depricated = Boolean.parseBoolean( depr ) ;
	}
	
	public ParsedCategory getCategory() {
		return category;
	}

	public void setCategory(ParsedCategory category) {
		this.category = category;
	}
	
	public boolean isIdent() {
		return isIdent;
	}
	
	
	public boolean isServiceProperty() {
		return isServiceProperty;
	}


	public static class ToggleDepricated implements IFilter{

		@Override
		public boolean accept(Object element) {
			return !((PropertyCharacteristic)element).getDepricated() ;
		}

		@Override
		public void addValueListener(IValueListener<?> listener) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void removeValueListener(IValueListener<?> listener) {
			// TODO Auto-generated method stub
			
		}		
	}
	
	public static class NameLabelProvider implements ITextLabelProvider{


		@Override
		public String getDescription(Object object) {
			return "" ;
		}

		@Override
		public String getText(IHasMeta meta, Object parent, Object object){
			if (object instanceof PropertyCharacteristic){
				return ((PropertyCharacteristic)object).getName() ;
			}
			if (object!=null){
			return object.toString();			
			}
			return "";
		}
		
	}
	
	public static class GroupFilter extends AbstractFilter implements IFilter {
		
		private static String currentGroup ; 

		public boolean accept(Object element)
		{
			if (element instanceof ITreeNode<?>){
				ITreeNode<?>el=(ITreeNode<?>) element;
				element=el.getElement();
			}
			if (element instanceof IClusterizationPoint<?>){
				return true;
			}
			final PropertyCharacteristic pc = (PropertyCharacteristic) element;
			return pc.getGroupNames().contains( currentGroup );
		}
	}

	public Collection<String> getCharacteristicsList()
	{
		ArrayList<String> result = new ArrayList<String>(storage.keySet()) ;
		result.add( CHAR_DEPRICATED_LABEL ) ;
		result.add( CHAR_ACCEPTS_DISAMBIGUATION ) ;
		result.add( CHAR_UNIQUE_VALUE_COUNT ) ;
		result.add( CHAR_GROUP_LABEL ) ;
		return result;
	}
	
	public PropertyCharacteristic processCategoryContent(){
		
		String name = this.getCharacteristic( CHAR_PROPERTY_NAME_LABEL ) ;
		
		if( this.getCharacteristic( PropertyCharacteristic.CHAR_PARSER_ID_LABEL ).equals(ParsedDimension.ID) )
		{
			HashMap<String,Integer> map = new HashMap<String, Integer>() ;
			for( ParsedDocument doc : category.getDocumentsList() )
			{
				ParsedProperty prop = doc.getProperty( name ) ;
				
				if( prop == null )
					continue ;
				
				for( ParsedValue val : prop.getValues() )
				{
					if( val == null )
						continue ;
					
					if( val instanceof ParsedDimension )
					{
						Unit unit = ((ParsedDimension)val).getUnit().getPrimaryUnit() ;
						String uName = unit.getShortName() ;
						Integer count = map.get( uName ) ;
						count = count != null ? count + 1 : 0 ;
						map.put( uName, count ) ;
					}					
				}
			}
			int maxCount = 0 ;
			String unitName = "";
			for( Entry<String, Integer> e : map.entrySet() )
			{
				Integer value = e.getValue() ;
				if( value > maxCount )
				{
					maxCount = value ;
					unitName = e.getKey() ;
				}
			}
			this.setCharacteristic( CHAR_UNIT_LABEL, unitName ) ;			
		}
		return this ;
	}
	
	private String getPrimaryUnitShortName() {
		String unitName = getCharacteristic(CHAR_UNIT_LABEL);
		String unitKind = getCharacteristic(CHAR_UNIT_KIND_LABEL);
		Unit unit = Units.getUnitByShortName( unitName,unitKind );
		
		if( unit != null ){
			Unit primaryUnit = unit.getPrimaryUnit();
			if( primaryUnit != null )
				return primaryUnit.shortName ;
		}

			return null ;
	}
	
	public boolean isMultivalue()
	{
		return multivalueProperties.contains( getCharacteristic(CHAR_PARSER_ID_LABEL)) ;
	}

}
